﻿using Facet.Combinatorics;
using Log;
using Solver;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Solver.Common;
using System.Diagnostics;
using Solver.Properties;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Threading;
using System.Windows.Threading;
using Core;
using Solver.Commands;
using Solver.ModelView;
using System.Collections;
using System.Windows;
using System.ComponentModel.Composition;

namespace SmileSolver
{    

    /// <summary>
    /// Hlavni resitel, ktery rozdava ukoly
    /// </summary>    
    [Export(typeof(Solver))]
    public class Solver : AbstractViewModel, ISolver
    {
        ILogger _log;
        SolutionCombination _cards = new SolutionCombination();
        TimerCore _timer;
        DateTime _start;
        int _max_task=4;
        int _start_task = 4;
        bool _working=false;

        ObservableCollection<ISolutionTask> _tasks = new ObservableCollection<ISolutionTask>();
        Dispatcher _dispatcher;

        [ImportingConstructor]
        public Solver(ILogger log, Dispatcher dispatcher)
        {
            _log = log;
            _dispatcher = dispatcher;
            _timer = new TimerCore();
            _timer.OnTick += _timer_OnTick;

            Progress = 0;
            
            string t_str = ConfigurationManager.AppSettings["MAX_TASK"];
            if (t_str != null)
            {
                _max_task = Convert.ToInt16(ConfigurationManager.AppSettings["MAX_TASK"]);
            }

            t_str = ConfigurationManager.AppSettings["START_TASK"];
            if (t_str != null)
            {
                _start_task = Convert.ToInt16(ConfigurationManager.AppSettings["START_TASK"]);
            }


            //_cards_combinations = new List<SolutionCombination>();
            loadCards();

            SolutionCombination zadani = new SolutionCombination();
            zadani.loadSolution(Resources.cards);

            SolutionCrossTask solution = new SolutionCrossTask(_log);
            solution.Name = "Start task";
            solution.FirstCombination(zadani);
            solution.State = TaskState.End;
            _tasks.Add(solution);          
        }

        /// <summary>
        /// Tikani casovace
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="span"></param>
        void _timer_OnTick(object sender, TimeSpan span)
        {
            Time = (DateTime.Now - _start);
        }

        public ISolutionTask GetTask(SolutionCombination combination, Type typ)
        {
            if (typ == typeof(SolutionCrossTask))
            {
                SolutionCrossTask solution = new SolutionCrossTask(_log);
                solution.Name = "Task"+combination.ID;// ID = poradi kombinace
                solution.OnComplete += OnComplete;
                solution.FirstCombination(combination);
                return solution;
            }

            // nevim cim resit
            return null;
        }

        /// <summary>
        /// Stopni ulohu
        /// </summary>
        public void Stop()
        {
            _working = false;
            _timer.Stop();
            _variace.Reset();
            GC.Collect();
        }

        // Spust hledani reseni hlavolamu
        public void Solve()
        {
            for (int i = _tasks.Count - 1; i >= 0; i--)
            {
                if (_tasks[i].Success == true)
                    _tasks.RemoveAt(i);
            }

            _start = DateTime.Now;
            _working = true;
            _timer.Start(new TimeSpan(0,1,0));

            Progress = _startCombination;
            //_start_idx = _startCombination;
            _total_counter = _startCombination;           

            NextTasks(_start_task);

            lock (_tasks)
            {
                foreach (ISolutionTask t in _tasks)
                {
                    if (t.State == TaskState.Stop)
                    {
                        t.Start();
                    }
                }
            }
        }

        public bool CanSolve()
        {
            if (_working == true)
                return false;

            return true;
        }

        // Udalost ... jeden ukol ISolutionTask dokoncil hledani
        void OnComplete(ISolutionTask task)
        {
            task.State = TaskState.End;
            task.OnComplete -= this.OnComplete;

            if (_gc_index++ > 16 *_max_task)
            {
                GC.Collect();
                _gc_index = 0;
            }

            if (task.Success == false)
            {
                _dispatcher.BeginInvoke(new Action(() =>
                {
                    _tasks.Remove(task);
                }));
            }
            else
            {
                // GC try clean memory
                GC.Collect();
                _dispatcher.Invoke(new Action(() =>
                    {
                        _tasks.Add(task);
                    }));
                _log.Info("Success:" + task.ToString());
                return;
            }

            if (task is SolutionGroup)
            {                
                NextTasks();                              
            }

        }

        //private int _start_idx;
        private int _total_counter=0;
        private int _gc_index; // index pro GC ... vynuceni cisteni pameti
        private IEnumerator _variace; // iterator
        /// <summary>
        /// Pripravit dalsi varku ukolů
        /// </summary>
        public void NextTasks(int pocet=1)
        {
            if (_working == false)
                return;

            List<ISolutionTask> list_gr = new List<ISolutionTask>();

            for (int c = 0; c < pocet; c++)
            {
                List<SolutionCombination> group_list = new List<SolutionCombination>();

                lock (_variace)
                {
                    for (int i = 0; i < _max_task; i++)
                    {
                        if (_variace.MoveNext() == true)
                        {
                            List<int> list = (List<int>)_variace.Current;
                            SolutionCombination new_s = CreateSolutionCombination(list, _total_counter);

                            group_list.Add(new_s);
                            Interlocked.Increment(ref _total_counter);                            
                        }
                        else
                        {
                            _log.Info("Finish work");
                            Stop();
                            break;
                        }
                    }
                }
                

                if (group_list.Count > 0)
                {

                    SolutionGroup sg = new SolutionGroup(group_list, _log);
                    sg.OnComplete += this.OnComplete;
                    //list_gr.Add(sg);
                    sg.Start();

                    if (_show_task_progress == true)
                    {                        
                        _dispatcher.Invoke(new Action(() =>
                        {
                            _tasks.Add(sg);
                        }));
                    }
                }
                //_start_idx += _max_task;                

            }

            double proc = 100 * ((double)_total_counter / (double)Total);
            Progress = (int)proc;
            //return list_gr;     
        }

        /// <summary>
        /// Read cars data from resource ... TXT data
        /// </summary>
        private void loadCards()
        {
            String data = Resources.cards;
            _cards.loadSolution(data);
            cardCombinations();
        }


        public void cardCombinations()
        {            
            Message = Resources.PrepareCombinations;
            Task prepare = new Task(new Action(() =>
            {
                List<SolutionCombination> cards_combinations = new List<SolutionCombination>();
                int[] karty = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
                Variations<int> variace = new Variations<int>(karty, 9, GenerateOption.WithoutRepetition);
                int cnt = (int)variace.Count;
                _variace =((IEnumerable) variace).GetEnumerator();                

                Message = String.Format(Resources.CardCombinations, cards_combinations.Count);
                Total = cnt;
                Progress = 100;
                OnPropertyChanged("StartCommand");
                //NextTasks();
            }));
            prepare.Start();
        }

        /// <summary>
        /// Vytvor SolutionCombination z variace
        /// </summary>
        /// <param name="array"></param>
        /// <param name="idx"></param>
        /// <returns></returns>
        private SolutionCombination CreateSolutionCombination(List<int> array, int idx)
        {
            SolutionCombination new_c = new SolutionCombination();
            new_c.ID = idx;
            
            foreach (int c in array)
            {
                OneCard new_card = new OneCard(_cards[c]);
                new_c.Add(new_card);
            }
            return new_c;

        }

        #region Properties

        // pole úloh
        public ObservableCollection<ISolutionTask> Tasks
        {
            get { return _tasks; }
        }

        
        private int _total=0;
        /// <summary>
        /// Pocet variacii
        /// </summary>
        public int Total 
        { 
            get {return _total; }
            set 
            { 
                _total = value;
                OnPropertyChanged("Total");
            }
        }

        bool _show_task_progress=false;
        public bool ShowTaskProgress
        {
            get { return _show_task_progress; }
            set 
            {
                _show_task_progress = value;
                OnPropertyChanged("ShowTaskProgress");
            }
        }

        public int TotalCounter
        {
            get { return _total_counter; }

        }

        TimeSpan _time = new TimeSpan();
        public TimeSpan Time
        {
            get { return _time; }
            set 
            {
                _time = value;
                OnPropertyChanged("Time");
            }
        }

        private int _startCombination=0;
        public int StartCombination
        {
            get { return _startCombination; }
            set 
            {
                _startCombination = value; 
                OnPropertyChanged("StartCombination");
            }
        }

        private int _progress;
        public int Progress 
        {
            get { return _progress; }
            set 
            {
                _progress = value;
                OnPropertyChanged("Progress");
                OnPropertyChanged("TotalCounter");
            }
        }

        private string _messsage="Ready";
        public string Message
        {
            get { return _messsage; }
            set 
            { 
                _messsage = value;
                OnPropertyChanged("Message");
            }
        }

        private ISolutionTask _selSolution;
        public ISolutionTask SelectedSolution
        {
            get { return _selSolution; }
            set 
            {
                _selSolution = value;
                //int hash = _selSolution.GetHashCode();
                OnPropertyChanged("SelectedSolution");
            }
        }

        #endregion

        #region Commands

        RelayCommand _startCommand;
        public ICommand StartCommand
        {
            get
            {
                if (_startCommand == null)
                {
                    _startCommand = new RelayCommand(param => this.Solve(), param=>this.CanSolve());
                }

                return _startCommand;
            }
        }

        #endregion
    }
}
